package edu.columbia.aicheckers.model;

import java.util.HashSet;
import java.util.Set;


/**
 * This class keeps track of the current state of the checker board
 * @author C.M.R
 *
 */
public class BoardState implements Cloneable, IBoardState{
	
	private static final int BOARD_SIZE = 32;
	Checker[] checkers = new Checker[BOARD_SIZE];
	private Color currentPly = Color.White;
	private Move changeStateMove;
	
	public int whiteCount = 0;
	public int blackCount = 0;
	
	public BoardState()
	{
		initialize();
	}
	
	public BoardState(int[] whitePositions, int[] blackPositions, Color currentPly)
	{
		for(int i = 0; i < whitePositions.length; i++)
		{
			checkers[whitePositions[i]-1] =  new Checker(Color.White, whitePositions[i]);
		}
		
		for(int i = 0; i < blackPositions.length; i++)
		{
			checkers[blackPositions[i]-1] =  new Checker(Color.Black, blackPositions[i]);
		}
		this.currentPly = currentPly;
	}
	
	protected BoardState(Checker[] checkers2, Color currentPly2) {
		this.checkers = checkers2;
		this.currentPly = currentPly2;
	}
	
	private void initialize()
	{
		for(int i = 0; i < 12; i++)
		{
			checkers[i] = new Checker(Color.Black, i+1);
		}
		
		for(int i = 20; i < 32; i++)
		{
			checkers[i] = new Checker(Color.White, i+1);
		}
	} 
	
	/* (non-Javadoc)
	 * @see edu.columbia.aicheckers.model.ICheckerBoard#getChecker(int)
	 */
	public Checker getChecker(int position)
	{
		return checkers[position-1];
	}
	
	public Color getCurrentPly()
	{
		return currentPly;
	}
	
	public boolean applyMove(Move move)
	{
		
		if(CheckersUtil.isValidMove(this,move))
		{
			this.apply(move);
			if(move != null && move.hasSkip())
			{
				// last move was a skip - see if there are available moves for that same checker
				Set<Move> moves = getAvailableMovesForPiece(move.getCheckerPiece());
				if(CheckersUtil.containsSkip(moves))
				{
					currentPly = move.getCheckerPiece().getColor();
					return true;
				}
			}
			currentPly = Color.getOponnentColor(move.getCheckerPiece().getColor());
			return true;
		}
		else
			return false;
		
	}
	
	private void apply(Move move) 
	{
		Checker checker = move.getCheckerPiece();
		int destination = move.getDestination();
		if(move.hasSkip())
		{
			if(checker.getColor().compareTo(Color.White) == 0)
			{
				whiteCount++;
			}
			else
			{
				blackCount++;
			}
			setEmpty(move.getSkippedLocation());
		}
		setEmpty(checker.getLocation());
		checker.location = destination;
		checkers[destination-1] = checker;
		if(checker.color == Color.White)
		{
			if(destination >= 1 && destination <= 4)
			{
				// Checker is now promoted to king status
				checker.isKing = true;
			}
		}
		else
		{
			if(destination >= 29 && destination <= 32)
			{
				// Checker is now promoted to king status
				checker.isKing = true;
			}
		}
	}

	
	private void setEmpty(int i)
	{
		checkers[i-1]=null;
	}
	
	/* (non-Javadoc)
	 * @see edu.columbia.aicheckers.model.ICheckerBoard#isEmpty(int)
	 */
	public boolean isEmpty(int i) {
		return checkers[i-1]==null;
	}

	/* (non-Javadoc)
	 * @see edu.columbia.aicheckers.model.ICheckerBoard#getColor(int)
	 */
	public Color getColor(int i) {
		if (isEmpty(i)) {
			return Color.Empty;
		} else
		{
			return getChecker(i).getColor();
		}
	}

	/* (non-Javadoc)
	 * @see edu.columbia.aicheckers.model.ICheckerBoard#isColor(int, edu.columbia.aicheckers.model.Color)
	 */
	public boolean isColor(int i, Color color) {
		return getColor(i) == color;
	}

	/* (non-Javadoc)
	 * @see edu.columbia.aicheckers.model.ICheckerBoard#isOpponentColor(int, edu.columbia.aicheckers.model.Checker)
	 */
	public boolean isOpponentColor(int skipped, Checker checker) {
		return isColor(skipped,Color.getOponnentColor(checker.getColor()));
	}
	
	StringBuilder sb = new StringBuilder();
	
	@Override
	public String toString()
	{
		int count = 0;
		sb.setLength(0);
		// n rows
		for(int n = 0;n < 8 ; n++)
		{
			// i columns
			for(int i = 0;i < 8; i++)
			{
				sb.append("|");
				if( ((n + i) % 2) == 0)
				{
					sb.append(" ");
				}
				else
				{
					if(checkers[count] == null)
						sb.append(" ");
					else if(checkers[count].getColor() == Color.White)
					{
						if(checkers[count].isKing())
							sb.append("O");
						else
							sb.append("o");
					}
					else
					{
						if(checkers[count].isKing())
							sb.append("X");
						else
							sb.append("x");
					}
					count++;
				}
				if(i == 7)
				{
					sb.append("|\n");
				}
			}
		}
		return sb.toString();
	}

	/* (non-Javadoc)
	 * @see edu.columbia.aicheckers.model.ICheckerBoard#getAvailableMoves(edu.columbia.aicheckers.model.Color)
	 */
	public Set<Move> getAvailableMoves(Color color) {
		
		Set<Move> moves = new HashSet<Move>();
		for(int i = 1; i <= 32; i++)
		{
			if(getColor(i)==color)
			{
				CheckersUtil.getNextAvailableMoves(this, getChecker(i), moves);
			}
		}
		CheckersUtil.processManadatorySkipMoves(moves);
		return moves;
	}
	
	public Set<Move> getAvailableMovesForPiece(Checker checker) {
		Set<Move> moves =  new HashSet<Move>();
		CheckersUtil.getNextAvailableMoves(this, checker, moves);
		return moves;
	}
	
	/* (non-Javadoc)
	 * @see edu.columbia.aicheckers.model.ICheckerBoard#simulateMove(edu.columbia.aicheckers.model.Move)
	 */
	public IBoardState simulateMove(Move move)
	{
		BoardState nextState = this.clone();
		nextState.applyMove(nextState.switchMove(move));
		return nextState;
	}
	
	protected Move switchMove(Move move)
	{
		int location = move.getCheckerPiece().getLocation();
		if(move.hasSkip())
		{
			move = new Move(this.getChecker(location),move.getDestination(),this.getChecker(move.getSkippedLocation()));
		}
		else
			move = new Move(this.getChecker(location),move.getDestination());
		return move;
	}
	
	public BoardState clone()
	{
		Checker[] otherCheckers = new Checker[BOARD_SIZE];
		for(int i = 0;i < otherCheckers.length; i++)
		{
			if(this.checkers[i] != null)
			{
				otherCheckers[i] = this.checkers[i].clone();
			}
		}
		return new BoardState(otherCheckers,currentPly);
	}

	public boolean hasCheckers(Color color) {
		for(int i = 1; i <= 32; i++)
		{
			if(getColor(i)==color)
			{
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Returns the number of checkers of a certain color in the current board state
	 * @param color
	 * @return
	 */
	public int getNumCheckers(Color color)
	{
		int numCheckers = 0;
		for (int i = 1; i <= BOARD_SIZE; i++)
		{
			if (getColor(i) == color)
			{
				numCheckers++;
			}
		}
		return numCheckers;
	}
}
